package page.mainPage;

import cn.leancloud.LCObject;
import com.alibaba.fastjson2.JSONObject;
import data.ActionModel;
import data.PageInfoModel;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Callback;
import org.jetbrains.annotations.NotNull;
import org.reflections.Reflections;
import page.BasePage;
import page.actionPage.ActionPage;
import utils.Constant;
import utils.ControllerUtils;
import utils.DBUtils;
import utils.GlobalVar;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.*;

public class MainPage extends page.BasePage {
    @FXML
    private ListView<ActionModel> actionListView;

    @FXML
    public HBox pageLocation;

    @FXML
    private Button pageSetting;

    DBUtils db;

    // 用于存储拖拽开始的对象
    private ActionModel startDragActionModel;
    private List<ActionModel> selectedActions;

    @FXML
    void initialize() {
        selectedActions = new ArrayList<>();
        db = new DBUtils(Constant.DB.ACTION_LIST);
        initGlobalData();
        initPageSetting();
        initActionList();
        refreshData();
    }

    public void initGlobalData() {
        List<LCObject> globalData = DBUtils.getAll(Constant.DB.GlOBAL_DATA);
        Map<String, LCObject> map = new HashMap<>();
        globalData.forEach(e -> map.put(e.getString(Constant.DB.KEY), e));
        GlobalVar.setLcGlobalMap(map);
    }

    public void initPageSetting() {
        // 若之前打开过页面，则从数据库中读取打开信息，否则新建数据
        List<PageInfoModel> openedPageList = GlobalVar.getGlobalArrayData(Constant.DB.OPENED_PAGE_LIST, PageInfoModel.class);
        if (openedPageList.size() == 0) {
            // 新建主页面
            LCObject lcObject = DBUtils.save(Constant.DB.PAGE, Constant.DB.PAGE_NAME, Constant.DB.MAIN, Constant.DB.DATA, new JSONObject());
            PageInfoModel pageInfoModel = new PageInfoModel();
            pageInfoModel.setLcObject(lcObject);
            db.put(Constant.DB.PAGE_NAME, Constant.DB.MAIN, pageInfoModel.getLcObject());
            openedPageList = new ArrayList<>(List.of(pageInfoModel));
            GlobalVar.setGlobalData(Constant.DB.OPENED_PAGE_LIST, openedPageList);
        }
        openedPageList.forEach(e -> {
            if (pageLocation.getChildren().size() != 0) {
                pageLocation.getChildren().add(new Text(">"));
            }
            pageLocation.getChildren().add(new Text(e.getLcObject().getString(Constant.DB.PAGE_NAME)));
        });
        pageSetting.setOnMouseClicked(mouseEvent -> {
            if (mouseEvent.getClickCount() == 2) {
                FXMLLoader pageSettingPage = new FXMLLoader(PageSettingPageController.class.getResource(Constant.MAIN_PAGE_PATH + "PageSettingPage.fxml"));
                try {
                    Parent parent = pageSettingPage.load();
                    Stage stage = ControllerUtils.createStage(parent);
                    stage.show();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    // 去除结尾的 7 个 null
    public int getActionListSize() {
        return actionListView.getItems().size() - 7;
    }

    public List<ActionModel> getActionList() {
        return actionListView.getItems().subList(0, getActionListSize());
    }

    public void refreshData() {
        // 从数据库中读取动作列表
        List<LCObject> actionList = DBUtils.getAll(Constant.DB.ACTION_LIST);
        // 根据 index 索引进行排序
        actionList.sort((o1, o2) -> {
            int o1Index = o1.getInt(Constant.DB.INDEX);
            int o2Index = o2.getInt(Constant.DB.INDEX);
            if (o1Index > o2Index) {
                return 1;
            } else if (o1Index < o2Index) {
                return -1;
            }
            // 没有相等情况，此行不会到达
            return 0;
        });
        actionListView.getItems().addAll(actionList.stream().map(ActionModel::new).toList());
        actionListView.getItems().addAll(null, null, null, null, null, null, null);
        // 在 list 最上方新建页面信息
        if (getActionListSize() == 0 || !db.getDataString(Constant.DB.TYPE, getActionList().get(0)).equals(Constant.TYPE.PAGE_INFO)) {
            db.addRow(JSONObject.of(Constant.DB.DATA, JSONObject.of(
                    Constant.DB.TYPE, Constant.TYPE.PAGE_INFO,
                    Constant.DB.PAGE_NAME, "页面信息|pageInfo",
                    Constant.DB.MAIN_PAGE_BUTTON_NAME, "页面信息")), 0);
        }


    }


    // 刷新 listview
    public void notifyListView(LCObject lcObject) {
        for (int i = 0; i < getActionListSize(); i++) {
            if (getActionList().get(i).getLcObject().equals(lcObject)) {
                getActionList().set(i, null);
                getActionList().set(i, getActionList().get(i));
                break;
            }
        }
    }

    /*
     * @Author Dongle
     * @Description 初始化动作列表
     * @Date 21:35 2023/1/9
     * @Param
     * @return
     **/
    public void initActionList() {
        actionListView.setOnKeyPressed(keyEvent -> {
            ActionModel focusedItem = actionListView.getFocusModel().getFocusedItem();
            if (focusedItem == null) {
                return;
            }
            if (keyEvent.getCode().getName().equals("Backspace")) {
                db.delete(focusedItem);
            }
        });
        actionListView.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> {
            if (keyEvent.getCode().equals(KeyCode.UP)) {
                ActionModel currentAction = selectedActions.get(0);
                int index = getActionList().indexOf(currentAction);
                if (index < getActionListSize()) {
                    actionListView.getFocusModel().focus(index - 1);
                }
            }
        });
        actionListView.focusedProperty().addListener((observableValue, aBoolean, focused) -> {
            if (focused) {
                ActionModel focusedAction = actionListView.getFocusModel().getFocusedItem();
                clearSelectedItem();
                // 如果点击了空白处，若最后一个action是文本则进入编辑状态，否则新建一个文本
                // 如果点击了非文本，则选中该行为
                if (focusedAction == null) {
                    if (db.getDataString(Constant.DB.TYPE, getActionList().get(getActionListSize() - 1)).equals(Constant.TYPE.TEXT)) {
                        selectedActions.add(getActionList().get(getActionListSize() - 1));
                        getActionList().get(getActionListSize() - 1).getBorderPane().getCenter().requestFocus();
                    } else {
                        db.addRow(getActionListSize());
                    }
                } else if (db.getDataString(Constant.DB.TYPE, focusedAction).equals(Constant.TYPE.ACTION)) {
                    focusedAction.getBorderPane().setStyle("-fx-border-color: #DC143C;-fx-border-width: 2");
                    focusedAction.getBorderPane().requestFocus();
                    selectedActions.add(focusedAction);
                    startDragActionModel = focusedAction;
                }
            }
        });
//        actionListView.addEventHandler(MouseEvent.MOUSE_PRESSED, k -> {
//            ActionModel focusedAction = actionListView.getFocusModel().getFocusedItem();
//            if(db.getDataString(focusedAction))
//        });
        actionListView.addEventHandler(MouseEvent.MOUSE_RELEASED, k -> startDragActionModel = null);
        // 设置触发动作
        actionListView.setCellFactory(new Callback<>() {
            @Override
            public ListCell<ActionModel> call(ListView<ActionModel> listView) {
                ListCell<ActionModel> cell = new ListCell<>() {
                    // 定制 listview 内容
                    @Override
                    protected void updateItem(ActionModel actionModel, boolean empty) {
                        super.updateItem(actionModel, empty);
                        if (empty || actionModel == null) {
                            // 数据为空时，设置不可见
                            setStyle("-fx-background-color: transparent;");
                            setGraphic(null);
                            return;
                        }
                        BorderPane borderPane;
                        if (actionModel.getBorderPane() == null) {
                            borderPane = new BorderPane();
                            actionModel.setBorderPane(borderPane);
                            if (db.getDataString(Constant.DB.TYPE, actionModel).equals(Constant.TYPE.TEXT)) {
                                // 新增空的选择器
                                borderPane.setCenter(generateActionSelector(actionModel));
                            } else if (db.getDataString(Constant.DB.TYPE, actionModel).equals(Constant.TYPE.PAGE_INFO)) {
                                Button centerButton = new Button(db.getDataString(Constant.DB.MAIN_PAGE_BUTTON_NAME, actionModel));
                                centerButton.setOnMouseClicked(mouseEvent -> {
                                    if (mouseEvent.getClickCount() == 2) {
                                        showPage(PageSettingPageController.class, actionModel);
                                    }
                                });
                                borderPane.setCenter(centerButton);
                            }
                            Button leftButton = new Button();
                            leftButton.setVisible(false);
                            leftButton.setText("+");
                            leftButton.setOnAction(actionEvent -> {
                                int currentIndex = listView.getItems().indexOf(actionModel);
                                db.addRow(currentIndex + 1);
                            });
                            borderPane.setLeft(leftButton);
                            borderPane.hoverProperty().addListener((observableValue, aBoolean, focused) -> borderPane.getLeft().setVisible(focused));
                        } else {
                            borderPane = actionModel.getBorderPane();
                        }
                        // 如果 action 已经初始化，则改变主界面的选择框为按钮
                        if (!(borderPane.getCenter() instanceof Button) &&
                                db.getDataString(Constant.DB.TYPE, actionModel).equals(Constant.TYPE.ACTION)) {
                            Button leftButton = new Button();
                            leftButton.setVisible(false);
                            leftButton.setText("...");
                            leftButton.setOnMouseClicked(mouseEvent -> showLeftMenu(leftButton, actionModel));
                            Button centerButton = new Button(db.getDataString(Constant.DB.MAIN_PAGE_BUTTON_NAME, actionModel));
                            centerButton.setOnMouseClicked(mouseEvent -> {
                                if (mouseEvent.getClickCount() == 2) {
                                    ActionPage actionActionPage = findActionByName(db.getDataString(Constant.DB.PAGE_NAME, actionModel));
                                    showPage(actionActionPage.getClass(), actionModel);
                                }
                            });
                            borderPane.setLeft(leftButton);
                            borderPane.setCenter(centerButton);
                        }
                        setGraphic(borderPane);
                    }

                    private void showLeftMenu(@NotNull Button leftButton, ActionModel actionModel) {
                        Scene buttonScene = leftButton.getScene();
                        Window buttonWindows = buttonScene.getWindow();
                        Point2D buttonPosition = leftButton.localToScene(0, 0);
                        Popup popShowList = new Popup();
                        int LIST_CELL_HEIGHT = 30;
                        ListView<String> leftMenuList = new ListView<>();
                        // 如果是设置信息按钮，只有下行新增选项
                        if (db.getDataString(Constant.DB.TYPE, actionModel).equals(Constant.TYPE.PAGE_INFO)) {
                            leftMenuList.getItems().addAll("下行新增");
                        } else {
                            leftMenuList.getItems().addAll("删除", "上行新增", "下行新增");
                        }
                        leftMenuList.setPrefWidth(150);
                        // 鼠标悬空时增加选中效果
                        leftMenuList.setCellFactory(new Callback<>() {
                            @Override
                            public ListCell<String> call(ListView<String> stringListView) {
                                Label label = new Label();
                                label.setPrefHeight(25);
                                label.setFont(new Font(12));
                                ListCell<String> cell = new ListCell<>() {
                                    @Override
                                    protected void updateItem(String item, boolean empty) {
                                        super.updateItem(item, empty);
                                        if (!empty && item != null) {
                                            label.setText(item);
                                            setGraphic(label);
                                        }
                                    }
                                };
                                cell.hoverProperty().addListener((observableValue, oldValue, newValue) -> {
                                    if (newValue && !label.getText().equals("")) {
                                        int position = stringListView.getItems().indexOf(label.getText());
                                        stringListView.getFocusModel().focus(position);
                                    }
                                });
                                return cell;
                            }
                        });
                        leftMenuList.setOnMouseClicked(event -> {
                            String selectedItem = leftMenuList.getSelectionModel().getSelectedItem();
                            // java14 以后，switch 不用加 break
                            switch (selectedItem) {
                                case "删除" -> db.delete(actionModel);
                                case "上行新增" -> {
                                    int currentIndex = listView.getItems().indexOf(actionModel);
                                    db.addRow(currentIndex);
                                }
                                case "下行新增" -> {
                                    int currentIndex = listView.getItems().indexOf(actionModel);
                                    db.addRow(currentIndex + 1);
                                }
                            }
                            popShowList.hide();
                        });

                        leftMenuList.setPrefHeight(leftMenuList.getItems().size() * LIST_CELL_HEIGHT + 10);
                        popShowList.getContent().addAll(leftMenuList);
                        popShowList.setAutoHide(true);
                        // 清空选择项
                        leftMenuList.getSelectionModel().clearSelection();
                        popShowList.show(
                                buttonWindows, buttonWindows.getX() + buttonScene.getX() + buttonPosition.getX() - leftMenuList.getPrefWidth()
                                , buttonWindows.getY() + buttonScene.getY() + buttonPosition.getY() - leftMenuList.getPrefHeight() / 2);
                    }
                };
                cell.setOnDragDetected(mouseEvent -> {
                    ActionModel actionModel = selectedActions.get(0);
                    if (actionModel == null || startDragActionModel == null) {
                        mouseEvent.consume();
                        return;
                    }
                    Dragboard dragBoard = cell.startDragAndDrop(TransferMode.MOVE);
                    ClipboardContent content = new ClipboardContent();
                    content.putString("");
                    dragBoard.setContent(content);
                });
                cell.setOnDragEntered(dragEvent -> {
                    Node graphic = cell.getGraphic();
                    if (graphic != null) {
                        graphic.setStyle("-fx-border-color: #DC143C;-fx-border-width: 2 0 0 0");
                    } else {
                        // 放在最后
                        getActionList().get(getActionListSize() - 1).getBorderPane().setStyle("-fx-border-color: #DC143C;-fx-border-width: 0 0 2 0");
                    }
                });
                cell.setOnDragExited(dragEvent -> {
                    Node graphic = cell.getGraphic();
                    if (graphic != null) {
                        cell.getItem().getBorderPane().setStyle("-fx-border-color: null;-fx-border-width: null");
                    } else {
                        getActionList().get(getActionListSize() - 1).getBorderPane().setStyle("-fx-border-color: null;-fx-border-width: null");
                    }
                });
                cell.setOnDragOver(dragEvent -> dragEvent.acceptTransferModes(TransferMode.MOVE));
                cell.setOnDragDropped(dragEvent -> {
                    // remove 后，selected 的值会变，所以提前保存一下
                    ActionModel startSelectedObj = startDragActionModel;
                    int startInde = getActionList().indexOf(startSelectedObj);
                    // 将要拖拽的元素放在目标位置前面
                    listView.getItems().remove(startDragActionModel);
                    int endIndex;
                    if (cell.getItem() != null) {
                        endIndex = listView.getItems().indexOf(cell.getItem());
                    } else {
                        // 放在最后
                        endIndex = getActionListSize();
                    }
                    // 向下拖拽替换时，目标元素要向上移动，因此需要清空上一个元素的样式
                    // 向上拖拽替换时，目标元素要向下移动，需要清空当前元素的样式
                    if (endIndex > startInde) {
                        getActionList().get(endIndex - 1).getBorderPane().setStyle("-fx-border-color: null;-fx-border-width: null");
                    } else {
                        getActionList().get(endIndex).getBorderPane().setStyle("-fx-border-color: null;-fx-border-width: null");
                    }
                    long actionIndex = DBUtils.getActionIndex(endIndex);
                    startSelectedObj.getLcObject().put(Constant.DB.INDEX, actionIndex);
                    startSelectedObj.getLcObject().save();
                    listView.getItems().add(endIndex, startSelectedObj);
                    listView.getSelectionModel().clearSelection();
                });
                return cell;
            }
        });
    }

    private void clearSelectedItem() {
        actionListView.getSelectionModel().clearSelection();
        // 清空已选列表
        for (ActionModel selectedAction : selectedActions) {
            selectedAction.getBorderPane().setStyle("-fx-border-color: null;-fx-border-width: null");
        }
        selectedActions.clear();
    }

    @FXML
    void runButton() {
        GlobalVar.getMainStage().setIconified(true);
        try {
            for (ActionModel actionModel : actionListView.getItems()) {
                // 跳过空白行
                if (actionModel == null) {
                    continue;
                }
                cn.leancloud.json.JSONObject data = actionModel.getLcObject().getJSONObject(Constant.DB.DATA);
                // 跳过文本输入
                if (data == null) {
                    continue;
                }
                ActionPage action = findActionByName(data.getString(Constant.DB.PAGE_NAME));
                // 跳过非动作
                if (action == null) {
                    continue;
                }
                action.run(JSONObject.parseObject(JSONObject.toJSONString(actionModel.getLcObject().get(Constant.DB.DATA))));
            }
        } catch (Exception e) {
            System.out.println("执行异常：" + e.getMessage());
            throw e;
        } finally {
            if (GlobalVar.driver != null) {
                GlobalVar.driver.quit();
            }
            GlobalVar.getMainStage().setIconified(false);
        }
    }

    /*
     * @Author Dongle
     * @Description 动作选择器
     * @Date 12:54 2022/6/3
     * @param mainCellData
     * @return javafx.scene.control.ComboBox<java.lang.String>
     **/
    public TextField generateActionSelector(ActionModel actionModel) {
        TextField textField = new TextField();
        textField.setPromptText("请输入文字或'/'进行选择");
        textField.setStyle("""
                -fx-background-color:  transparent, white, transparent, white;
                -fx-background-insets: 0;
                -fx-background-radius: 0;""");
        Collection<ActionPage> actions = getActionMap().values();
        List<String> actionNames = new ArrayList<>();
        actions.forEach(action -> {
            if (action.getTYPE().equals(Constant.TYPE.ACTION)) {
                actionNames.add(action.getComboxName());
            }
        });
        AutoCompleteTextField autoCompleteTextField = new AutoCompleteTextField(textField, actionNames, actionName -> {
            // 选择一项后的动作
            HashMap<String, ActionPage> hashMap = getActionMap();
            ActionPage actionActionPage = hashMap.get(actionName);
            // 加载action
            showPage(actionActionPage.getClass(), actionModel);
        });
        textField.focusedProperty().addListener((observableValue, aBoolean, focused) -> {
            if (focused) {
                clearSelectedItem();
            }
        });
        textField.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> {
            // 光标在第一位敲下 backspace 可删除本行
            int caretPosition = textField.getCaretPosition();
            if (caretPosition == 0 && keyEvent.getCode().getName().equals("Backspace")) {
                db.delete(actionModel);
                // 关闭已经打开的提示窗口
                autoCompleteTextField.closePopShowList();
            }
            // 光标在文本末尾，敲下 enter 可新增一行
            else if (caretPosition > textField.getLength() - 1 && keyEvent.getCode().getName().equals("Enter")) {
                int currentIndex = actionListView.getItems().indexOf(actionModel);
                db.addRow(currentIndex + 1);
            }
        });
        return textField;
    }


    /*
     * @Author Dongle
     * @Description 通过反射取出目录 actionPage 下面的所有子类
     * @Date 21:35 2023/1/9
     * @Param
     * @return
     **/
    public HashMap<String, ActionPage> getActionMap() {
        // 通过反射取出目录 actionPage 下面的所有子类，向选择框中添加子类信息
        Reflections actionReflections = new Reflections("page.actionPage");
        // 取出继承 ActionBasePage 的所有动作
        Set<Class<? extends ActionPage>> actionClass = actionReflections.getSubTypesOf(ActionPage.class);
        HashMap<String, ActionPage> actionMap = new HashMap<>();
        actionClass.forEach(a -> {
            try {
                ActionPage action = a.getDeclaredConstructor().newInstance();
                actionMap.put(action.getComboxName(), action);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
        return actionMap;
    }

    public ActionPage findActionByName(String actionName) {
        return getActionMap().getOrDefault(actionName, null);
    }

    /*0
     * @Author Dongle
     * @Description 将 action 套上模板
     * @Date 20:09 2022/12/27
     * @Param
     * @param fxmlPath action 的 xml 位置
     * @param pageData 通用数据
     * @return
     **/
    public void showPage(Class<?> controllerClass, ActionModel actionModel) {
        try {
            URL url = controllerClass.getResource(controllerClass.getSimpleName().replace("Controller", ".fxml"));
            // 加载 actionPage
            FXMLLoader fxmlLoader = new FXMLLoader(url);
            Parent parent = fxmlLoader.load();
            BasePage controller = fxmlLoader.getController();
            controller.setLcObject(actionModel.getLcObject());
            controller.basePageInit();
            Stage stage = ControllerUtils.createStage(parent);
            stage.show();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


}

